package com.khaled.protclass.server.servlet;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.mail.MessagingException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.log4j.Logger;

import com.khaled.protclass.model.bean.prediction.PredictionForm;
import com.khaled.protclass.model.bean.prediction.PredictionResult;
import com.khaled.protclass.model.prediction.Prediction;
import com.khaled.protclass.server.bean.RequestForm;
import com.khaled.protclass.server.common.Configuration;
import com.khaled.protclass.server.common.Constants;
import com.khaled.protclass.server.common.HTMLEmail;

/**
 * Servlet implementation class VirulentCDServlet
 */
public class VirulentCDServlet extends HttpServlet
{
	private static final long serialVersionUID = 1L;

	/** Logger. */
	private static Logger logger = Logger.getLogger(VirulentCDServlet.class);

	private static final String SERVER_NAME = "Virulent-CD";

	/**
	 * Default constructor.
	 */
	public VirulentCDServlet()
	{
		// TODO Auto-generated constructor stub
	}

	@Override
	public void init() throws ServletException
	{
		logger.info("Start init.");
		try
		{
			logger.info("Loading Configurations.");
			Configuration.getInstance().loadConfiguration(getServletContext().getRealPath("/WEB-INF") + "\\" + SERVER_NAME);
			logger.info("The configurations are loaded.");
		}
		catch (Exception e)
		{
			logger.error(e);
		}

		super.init();
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
	{
		logger.info("Start doPost");
		final Configuration config = Configuration.getInstance();
		try
		{
			// 1. Get parameters from request
			final RequestForm form = getRequestParameters(request);
			logger.info("Request parameters are loaded.");
			// 2. Validate Form parameters
			ArrayList<String> errorMessages = form.validate();
			if (errorMessages.size() > 0)
			{
				logger.info("Error in the request parameters, dispatch to error.jsp page.");
				// 2.1 dispatch to Error JSP view
				request.setAttribute("ErrorMessages", errorMessages);
				RequestDispatcher errorView = request.getRequestDispatcher("errors.jsp");
				errorView.forward(request, response);
			}
			else
			{
				// 3. Inform the user that the results will be sent to his email
				// TODO: New option for providing results in a jsp page.
				logger.info("There is no error in the parameters, dispatch to the results.jsp page.");
				request.setAttribute("CustomerEmail", form.getEmail());
				RequestDispatcher view = request.getRequestDispatcher("result.jsp");
				view.forward(request, response);

				new Thread()
				{
					public void run()
					{
						// 4. Pass parameters to model
						Prediction model;
						try
						{

							logger.info("Start the processing thread.");
							logger.info("Get the model.");
							model = Prediction.getPrediction();

							// 5. Get the prediction results
							PredictionForm predForm = new PredictionForm(form.getProteinList(), form.getPredictionApproach().getFeatures(), form
									.getPredictionApproach().getClassifier(), form.getSpecificity(), config.get("class.1"), config.get("class.2"));
							logger.info("Send the request form to the model and predict.");
							ArrayList<PredictionResult> predictionResult = model.predict(predForm);
							logger.info("Prediction finished.");

							// 6. Send the prediction results to customer email
							HTMLEmail email = HTMLEmail.getInstance();
							ArrayList<String> errorMessages = form.validate();
							if (errorMessages.size() > 0)
							{
								logger.info("Error during prediction, will be sent to the customer email.");
								// there are errors encountered during
								// prediction, so send error mail.
								email.sendError(errorMessages, form.getEmail());
							}
							else
							{
								logger.info("Results will be sent to the customer email.");
								// no errors during prediction, so now send the
								// prediction results
								email.send(predictionResult, form.getEmail());
								logger.info("Results sent.");
							}

						}
						catch (MessagingException e)
						{
							logger.error(e);
						}
						catch (Exception e)
						{
							logger.error(e);
							try
							{
								HTMLEmail email = HTMLEmail.getInstance();

								ArrayList<String> errors = new ArrayList<String>();
								errors.add("Internal Server Error.");

								email.sendError(errors, form.getEmail());
							}
							catch (MessagingException e1)
							{
								logger.error(e1);
							}
						}
					};
				}.start();
			}
		}
		catch (Exception e)
		{
			logger.error(e);
		}
	}

	private RequestForm getRequestParameters(HttpServletRequest request) throws Exception
	{
		RequestForm form = new RequestForm();

		FileItemFactory factory = new DiskFileItemFactory();

		// Create a new file upload handler
		ServletFileUpload upload = new ServletFileUpload(factory);

		@SuppressWarnings("rawtypes")
		List items = upload.parseRequest(request);

		@SuppressWarnings("rawtypes")
		Iterator iter = items.iterator();

		while (iter.hasNext())
		{
			FileItem item = (FileItem) iter.next();
			if (Constants.HTML_FORM_PROTEINS_PASTE.equals(item.getFieldName()))
			{
				form.setProteinsSequence(item.getString().toUpperCase());
			}
			else if (Constants.HTML_FORM_PROTEINS_UPLOAD.equals(item.getFieldName()))
			{
				String fileContent = item.getString().trim().toUpperCase();
				if (!"".equals(fileContent))
				{
					form.setProteinsSequence(fileContent);
				}
			}
			else if (Constants.HTML_FORM_APPROACH.equals(item.getFieldName()))
			{
				form.setPredictionApproach(item.getString().toUpperCase());
			}
			else if (Constants.HTML_FORM_SPECIFICITY.equals(item.getFieldName()))
			{
				form.setSpecificity(Double.parseDouble(item.getString()) / (double) 100);
			}
			else if (Constants.HTML_FORM_EMAIL.equals(item.getFieldName()))
			{
				form.setEmail(item.getString());
			}
		}

		return form;
	}

}
